package com.maxd.service.activity;

import com.google.common.collect.Lists;
import com.maxd.adapter.LoginUserHolder;
import com.maxd.bean.PageRes;
import com.maxd.bean.activity.ActivityJoinData;
import com.maxd.bean.activity.JoinActivityReq;
import com.maxd.constants.PayScene;
import com.maxd.error.DescribeException;
import com.maxd.error.ExceptionEnum;
import com.maxd.model.ActivityJoinRecord;
import com.maxd.model.PayRecord;
import com.maxd.model.RwActivity;
import com.maxd.respository.activitties.ActivityJoinRecordRepository;
import com.maxd.respository.activitties.RwActivityRepository;
import com.maxd.service.pay.IPayRecordService;
import com.maxd.utils.AssertUtil;
import com.maxd.utils.JsonUtils;
import com.maxd.utils.PageUtils;
import com.maxd.utils.SeqUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class RwActivityService implements IRwActivityService {

    private static final Logger log = LoggerFactory.getLogger(RwActivityService.class);
    @Autowired
    private RwActivityRepository rwActivityRepository;

    @Autowired
    private ActivityJoinRecordRepository activityJoinRecordRepository;

    @Autowired
    private IPayRecordService payRecordService;

    @Override
    public void save(RwActivity rwActivity) {
        // 参数校验，暂时不做
        if(rwActivity.getId() == null) {
            // 新增活动
            rwActivity.setJoinMembers(0);
            rwActivity.setCreateBy(LoginUserHolder.getAdminId());
            rwActivity.setUpdateBy(LoginUserHolder.getAdminId());
        } else {
            RwActivity oldRec = rwActivityRepository.findById(rwActivity.getId()).orElse(null);
            log.info("查询到活动信息:{}", JsonUtils.toJsonString(oldRec));
            AssertUtil.notNull(oldRec, ExceptionEnum.ACTIVITY_NOF);
            // 防止在修改的时候，有人报名
            rwActivity.setJoinMembers(oldRec.getJoinMembers());
            rwActivity.setUpdateBy(LoginUserHolder.getAdminId());
            rwActivity.setUpdateTime(new Date());
        }
        rwActivityRepository.save(rwActivity);
    }

    @Override
    public RwActivity detail(Long id) {
        // 查询活动信息
        RwActivity rwActivity = rwActivityRepository.findById(id).orElse(null);
        log.info("查询到活动信息:{}", JsonUtils.toJsonString(rwActivity));

        AssertUtil.notNull(rwActivity, ExceptionEnum.ACTIVITY_NOF);

        return rwActivity;
    }

    @Override
    public PageRes<RwActivity> list(Integer pageNo, Integer pageSize, String name, String activityType, Date endTimeMin) {
        // 分页参数
        Sort.Order order = new Sort.Order(Sort.Direction.DESC, "createTime");
        Pageable pageable = PageRequest.of(pageNo, pageSize, Sort.by(order));
        Specification<RwActivity> queryCondition = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = Lists.newArrayList();
            if(StringUtils.isNotEmpty(name)) {
                predicates.add(criteriaBuilder.like(root.get("name"), "%" + name + "%"));
            }
            if(StringUtils.isNotEmpty(activityType)) {
                predicates.add(criteriaBuilder.equal(root.get("activityType"), activityType));
            }
            if(endTimeMin != null) {
                predicates.add(criteriaBuilder.greaterThan(root.get("endTime"), endTimeMin));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<RwActivity> pageList = rwActivityRepository.findAll(queryCondition, pageable);
        return PageUtils.toPageRes(pageList);
    }

    @Override
    @Transactional
    public ActivityJoinRecord join(JoinActivityReq req) {
        log.info("参加活动入参:{}", JsonUtils.toJsonString(req));
        // 登录校验
        Long userId = LoginUserHolder.getUserId();
        AssertUtil.assertTrue(userId != null && userId > 0, ExceptionEnum.WRONT_TOKEN);
        // 查询活动信息
        RwActivity rwActivity = rwActivityRepository.findById(req.getActivityId()).orElse(null);
        log.info("查询到活动信息:{}", JsonUtils.toJsonString(rwActivity));

        AssertUtil.notNull(rwActivity, ExceptionEnum.ACTIVITY_NOF);
        // 活动是否已过期
        AssertUtil.assertTrue(new Date().before(rwActivity.getEndTime()), ExceptionEnum.ACTIVITY_EXPIRED);
        // 金额是否匹配，暂不考虑优惠券、积分抵扣的情况
        BigDecimal expectAmt = rwActivity.getPrice().multiply(new BigDecimal(req.getNum()));
        AssertUtil.assertTrue(expectAmt.compareTo(req.getAmount()) == 0, ExceptionEnum.PAY_MONEY_ERROR);
        // 校验并增加参与人数
        Integer rows = rwActivityRepository.joinWithCheck(req.getActivityId(), req.getNum());
        if(rows < 0) {
            throw new DescribeException(ExceptionEnum.ACTIVITY_FULLED);
        }
        ActivityJoinRecord record = buildJoinRecord(req, rwActivity);
        if(BigDecimal.ZERO.compareTo(req.getAmount()) < 0) {
            // 需要支付
            PayRecord payRecord = payRecordService.savePayRecord(record.getTransId(), req.getAmount(), record.getActivityName(), PayScene.ACTIVITY_PAY);
            record.setPaySeq(payRecord.getPaySeq());
        }
        activityJoinRecordRepository.save(record);
        return record;
    }

    @Override
    public PageRes<ActivityJoinData> joinList(Integer pageNo, Integer pageSize, Long userId, Long activityId, String status) {

        // 分页参数
        Sort.Order order = new Sort.Order(Sort.Direction.DESC, "createTime");
        Pageable pageable = PageRequest.of(pageNo, pageSize, Sort.by(order));
        Specification<ActivityJoinRecord> queryCondition = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = Lists.newArrayList();
            if(userId != null) {
                predicates.add(criteriaBuilder.equal(root.get("userId"), userId));
            }
            if(activityId != null) {
                predicates.add(criteriaBuilder.equal(root.get("activityId"), activityId));
            }
            if(StringUtils.isNotEmpty(status)) {
                predicates.add(criteriaBuilder.equal(root.get("status"), status));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<ActivityJoinRecord> pageList = activityJoinRecordRepository.findAll(queryCondition, pageable);
        List<ActivityJoinRecord> joinRecords = pageList.getContent();
        if(CollectionUtils.isEmpty(joinRecords)) {
            return PageRes.empty();
        }
        List<Long> activityIds = joinRecords.stream().map(e -> e.getActivityId()).collect(Collectors.toList());
        List<RwActivity> activitys = rwActivityRepository.findAllById(activityIds);
        Map<Long, RwActivity> activityMap = activitys.stream().collect(Collectors.toMap(e -> e.getId(), e -> e, (e1, e2) -> e2));

        List<ActivityJoinData> dataList = Lists.newArrayList();
        for (ActivityJoinRecord joinRecord : joinRecords) {
            ActivityJoinData activityJoinData = new ActivityJoinData();
            BeanUtils.copyProperties(joinRecord, activityJoinData);
            RwActivity rwActivity = activityMap.get(joinRecord.getActivityId());
            if(rwActivity != null) {
                activityJoinData.setDescription(rwActivity.getDescription());
                activityJoinData.setActivityAddress(rwActivity.getActivityAddress());
                activityJoinData.setCoverImg(rwActivity.getCoverImg());
                activityJoinData.setPrice(rwActivity.getPrice());
                activityJoinData.setTotalMembers(rwActivity.getTotalMembers());
                activityJoinData.setJoinMembers(rwActivity.getJoinMembers());
                activityJoinData.setStartTime(rwActivity.getStartTime());
                activityJoinData.setEndTime(rwActivity.getEndTime());
                activityJoinData.setContactName(rwActivity.getContactName());
                activityJoinData.setContactPhone(rwActivity.getContactPhone());
                activityJoinData.setActivityDetail(rwActivity.getActivityDetail());
                activityJoinData.setActivityType(rwActivity.getActivityType());
            }
            dataList.add(activityJoinData);
        }

        return new PageRes<>(pageList.getTotalElements(), dataList);

    }

    @Override
    public void cancel(String transId, Long userId) {
        ActivityJoinRecord record = findByTransId(transId);
        if(record == null || !record.getUserId().equals(userId)) {
            throw new DescribeException(ExceptionEnum.JOIN_RECORD_NOF);
        }
        if("3".equals(record.getStatus())) {
            log.info("活动参加记录已经被取消,无需重复处理.");
            return;
        }
        if("2".equals(record.getStatus())) {
            log.info("活动参加记录已经支付成功,不允许取消.");
            throw new DescribeException(ExceptionEnum.DATA_CHANGED);
        }
        // 回退参与人数
        rwActivityRepository.cancel(record.getActivityId(), record.getNum());
        // 更新记录状态
        activityJoinRecordRepository.updateStatus(transId, "3");
    }

    private ActivityJoinRecord buildJoinRecord(JoinActivityReq req, RwActivity rwActivity) {
        ActivityJoinRecord record = new ActivityJoinRecord();
        record.setTransId(SeqUtils.dateSeq());
        record.setUserId(LoginUserHolder.getUserId());
        record.setUserPhone(LoginUserHolder.getUserInfo().getPhone());
        record.setActivityId(rwActivity.getId());
        record.setActivityName(rwActivity.getName());
        record.setAmount(req.getAmount());
        record.setPrice(rwActivity.getPrice());
        record.setNum(req.getNum());
        record.setJoinPhone(req.getContactPhone());
        record.setJoinName(req.getUserName());

        record.setStatus(BigDecimal.ZERO.equals(req.getAmount()) ? "2" : "0");
        record.setCreateBy(String.valueOf(record.getUserId()));
        record.setUpdateBy(String.valueOf(record.getUserId()));
        return record;
    }

    public ActivityJoinRecord findByTransId(String transId) {
        ActivityJoinRecord param = new ActivityJoinRecord();
        param.setTransId(transId);
        return activityJoinRecordRepository.findOne(Example.of(param)).orElse(null);
    }
}
